// HSS.AsynResultNoResult.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license
// http://www.opensource.org/licenses/mit-license.html
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2010
// ----------------------------------------------------------------------------
// File:       AsynResultNoResult.cs
// Author:     HSS\gbanta
// Created:    08/12/2010
// Modified:   12/04/2010
// ----------------------------------------------------------------------------
namespace HSS
{
	#region Using Directives
	using System;
	using System.Threading;
	#endregion

	/// <summary>
	/// A basic implementation of <see cref="System.IAsyncResult"/> with out any results
	/// </summary>
	/// <remarks>Courtesy of Jeffrey Richter (http://msdn.microsoft.com/msdnmag/issues/07/03/ConcurrentAffairs/)</remarks>
	public class AsyncResultNoResult : IAsyncResult
	{
		#region Fields

		// Fields set at construction which never change while operation is pending
		private readonly AsyncCallback _asyncCallback;

		// Fields set at construction which do change after operation completes
		private const Int32 c_StatePending = 0;
		private const Int32 c_StateCompletedSynchronously = 1;
		private const Int32 c_StateCompletedAsynchronously = 2;
		private Int32 _CompletedState = c_StatePending;

		// Field that may or may not get set depending on usage
		private ManualResetEvent _asyncWaitHandle;
		private Object _asyncState;

		// Fields set when operation completes
		private Exception _exception;

		#endregion

		#region Constructor
		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="asyncCallback">The AsyncCallback callback</param>
		public AsyncResultNoResult(AsyncCallback asyncCallback)
		{
			_asyncCallback = asyncCallback;
			_asyncState = null;
		}
		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="asyncCallback">The AsyncCallback callback</param>
		/// <param name="state">Any object state</param>
		public AsyncResultNoResult(AsyncCallback asyncCallback, Object state)
		{
			_asyncCallback = asyncCallback;
			_asyncState = state;
		}
		#endregion

		/// <summary>
		/// Indicate that the operation is complete
		/// </summary>
		/// <param name="exception">Any exceptions encountered or null</param>
		/// <param name="completedSynchronously">Did it complete Synchronously</param>
		public void SetAsCompleted(Exception exception, Boolean completedSynchronously)
		{
			// Passing null for exception means no error occurred. 
			// This is the common case
			_exception = exception;

			// The _CompletedState field MUST be set prior calling the callback
			int SyncOrAsync = completedSynchronously ? c_StateCompletedSynchronously : c_StateCompletedAsynchronously;
			Int32 prevState = Interlocked.Exchange(ref _CompletedState, SyncOrAsync);

			if (prevState != c_StatePending)
				throw new InvalidOperationException("You can set a result only once");

			// If the event exists, set it
			if (_asyncWaitHandle != null) _asyncWaitHandle.Set();

			// If a callback method was set, call it
			if (_asyncCallback != null) _asyncCallback(this);
		}
		/// <summary>
		/// End the async operation, waiting for it to complete
		/// </summary>
		public void EndInvoke()
		{
			// This method assumes that only 1 thread calls EndInvoke 
			// for this object
			if (!IsCompleted)
			{
				// If the operation isn't done, wait for it
				AsyncWaitHandle.WaitOne();
				AsyncWaitHandle.Close();
				_asyncWaitHandle = null;  // Allow early GC
			}

			// Operation is done: if an exception occured, throw it
			if (_exception != null) throw _exception;
		}

		#region Implementation of IAsyncResult
		/// <summary>
		/// Gets the associated object state
		/// </summary>
		public Object AsyncState { get { return _asyncState; } set { _asyncState = value; } }
		/// <summary>
		/// Did the operation complete Async
		/// </summary>
		public Boolean CompletedSynchronously
		{
			get
			{
				return Thread.VolatileRead(ref _CompletedState) ==
					c_StateCompletedSynchronously;
			}
		}
		/// <summary>
		/// Gets the WaitHandle associated with this result
		/// </summary>
		public WaitHandle AsyncWaitHandle
		{
			get
			{
				if (_asyncWaitHandle == null)
				{
					Boolean done = IsCompleted;
					ManualResetEvent mre = new ManualResetEvent(done);
					if (Interlocked.CompareExchange(ref _asyncWaitHandle, mre, null) != null)
					{
						// Another thread created this object's event; dispose 
						// the event we just created
						mre.Close();
					}
					else
					{
						if (!done && IsCompleted)
						{
							// If the operation wasn't done when we created 
							// the event but now it is done, set the event
							_asyncWaitHandle.Set();
						}
					}
				}
				return _asyncWaitHandle;
			}
		}
		/// <summary>
		/// Is the Async Operation Completed
		/// </summary>
		public Boolean IsCompleted
		{
			get { return Thread.VolatileRead(ref _CompletedState) != c_StatePending; }
		}
		#endregion
	}

}